#include <cstdio>
#include <cstdlib>
#include <memory>
#include <sstream>
#include <iostream>

class StackChecker {
public:
    StackChecker()
    {
        int64_t bp;
        __asm__ __volatile__ ("mov %0, x29\n" : "=g"(bp)::"memory");
        callerBp = (int64_t *)*(int64_t *)bp;

        int64_t *nextBp = callerBp;
        do {
            firstBp = nextBp;
            nextBp = (int64_t *)*(int64_t *)nextBp;
        } while (nextBp != nullptr);

        firstStack = std::make_unique<int64_t[]>(firstBp - callerBp);

        int i = 0;
        for (int64_t *p = callerBp; p < firstBp; p++) {
            firstStack[p - callerBp] = *p;
        }
    }
    ~StackChecker() = default;

    void Check(int file)
    {
        std::stringstream ss;
        bool have = false;
        for (int64_t *p = callerBp; p < firstBp; p++) {
            if (firstStack[p - callerBp] != *p) {
                ss << "    "
                    << std::showbase << (void *)p << std::noshowbase
                    << " stack+" << std::dec << (p - callerBp) * sizeof(int64_t)
                    << " changed, old: " << std::hex << firstStack[p - callerBp]
                    << ", now: " << std::hex << *p << std::endl;
                have = true;
            }
        }
        if (have) {
            std::cout << "line<" << std::dec << file << "> found stack overflow!" << std::endl;
            std::cout << ss.str() << std::endl;
        } else {
            std::cout << "line<" << std::dec << file << "> stack check ok!" << std::endl;
        }
    }

private:
    std::unique_ptr<int64_t[]> firstStack = nullptr;
    int64_t *firstBp = nullptr;
    int64_t *callerBp = nullptr;
};

void func(int32_t a)
{
#define OFFSET (10)
    int64_t i = 0x234;
    int64_t old_canary;

    StackChecker sc;
    sc.Check(__LINE__);
    printf("%016lx\n", *(&i + OFFSET));
    sc.Check(__LINE__);
    old_canary = *(&i + OFFSET);
    sc.Check(__LINE__);
    *(&i + OFFSET) = 0;
    sc.Check(__LINE__);
    printf("%016lx\n", *(&i + OFFSET));
    sc.Check(__LINE__);
    *(&i + OFFSET) = old_canary;
    sc.Check(__LINE__);
}

int main()
{
    StackChecker sc;
    system("cat /proc/$(($$ - 1))/maps");
    sc.Check(__LINE__);
    func(0x123);
    sc.Check(__LINE__);
    return 0;
}
